#include "_2task.h"
#include "astralapi.h"
#include "bitmapapi.h"
#include "strapi.h"



static int do_pool_work(object arg)
{
    int ret = -1;
    int index = -1;
    runable fn = NULL;
    object jarg = NULL;
    AstralTaskPool pool = CAST_AS(AstralTaskPool, arg);
    AstralTask self = get_curr_task();

    while (1)
    {
        release_sem(&pool->psem);
        obtain_sem(&pool->gsem);

        while (!arch_compare_and_swap(&pool->player, 0, 1)){}
        index = pool->gpointer;
        if (index == pool->nr_thread - 1)
        {
            pool->gpointer = 0;
        }
        else
        {
            pool->gpointer = index + 1;
        }
        while (!arch_compare_and_swap(&pool->player, 1, 0)){}

        kprint("[%s] job started on %d @%lld\n",
               self->name,
               arch_get_cpuid(),
               get_tick_count());

        fn = pool->job_workers[index].job.entry;
        jarg = pool->job_workers[index].job.arg;
        ret = fn(jarg);
        pool->job_workers[index].job.ret = ret;
        pool->job_workers[index].job.status = DONE;

        kprint("[%s] job finished on %d @%lld\n",
               self->name,
               arch_get_cpuid(),
               get_tick_count());
    }
    return 0;
}


AstralTaskPool create_task_pool(const char *name, int nr_thread, int prio)
{
    AstralTaskPool pool = NULL;
    char localname[15];
    int size = 0;

    size = sizeof(struct AstralTaskPool) + 
           sizeof(struct PoolJob) * nr_thread +
           sizeof(struct PoolWoker) * nr_thread;

    pool = CAST_AS(AstralTaskPool, ff_alloc(size));

    if (!pool)
    {
        goto error;
    }

    for (int i = 0; i < nr_thread; i++)
    {
        StackPointer sp = CAST_AS(StackPointer, ff_alloc(1024));
        if (!sp)
        {
            kprint("no memory\n");
            // release all memory requested previous
            goto error;
        }
        pool->job_workers[i].worker.init_sp = sp;
    }

    for (int i = 0; i < nr_thread; i++)
    {
        snprintf(localname, 15, "%s-%-03d", name, i);
        struct AstralAttr attr = {
            .base = pool->job_workers[i].worker.init_sp,
            .stack_size = 1024,
            .priority = prio,
            .name = localname};
        create_task(&pool->job_workers[i].worker.thread, &attr, do_pool_work, pool);
    }

    pool->nr_thread = nr_thread;
    pool->player = 0;
    pool->gpointer = 0;
    pool->ppointer = 0;
    create_sem(&pool->psem);
    create_sem(&pool->gsem);

    disable_preempt(arch_get_cpuid());
    for (int i = 0; i < nr_thread; i++)
    {
        distach_task(&pool->job_workers[i].worker.thread);
    }
    restore_preempt(arch_get_cpuid());

error:
    // process
    return pool;
}

void pool_dispatch_job(AstralTaskPool pool, runable job, object arg)
{
    obtain_sem(&pool->psem);

    while(!arch_compare_and_swap(&pool->player, 0, 1));
    int ptr = pool->ppointer;
    if(ptr == pool->nr_thread - 1)
    {
        pool->ppointer = 0;
    }
    else
    {
        pool->ppointer++;
    }
    while(!arch_compare_and_swap(&pool->player, 1, 0));

    pool->job_workers[ptr].job.arg = arg;
    pool->job_workers[ptr].job.entry = job;
    pool->job_workers[ptr].job.status = VALID;
    pool->job_workers[ptr].job.ret = 0;

    release_sem(&pool->gsem);
}

